use std::fmt::Display;

use crate::Validator;

pub trait Comparable: PartialOrd {}

impl Comparable for i8 {}
impl Comparable for u8 {}
impl Comparable for i16 {}
impl Comparable for u16 {}
impl Comparable for i32 {}
impl Comparable for isize {}
impl Comparable for u32 {}
impl Comparable for f32 {}
impl Comparable for usize {}
impl Comparable for i64 {}
impl Comparable for u64 {}
impl Comparable for f64 {}
impl Comparable for i128 {}
impl Comparable for u128 {}

pub struct RangeValidator<T: Comparable> {
    pub min: T,
    pub max: T,
}

impl<T: Comparable + Display> Validator<T> for RangeValidator<T> {
    type Error = String;

    fn validate(
        &self,
        ident: Option<&str>,
        name: Option<&str>,
        data: &T,
    ) -> Result<(), Self::Error> {
        if self.min > *data || self.max < *data {
            return Err(range_error(ident, name, data, &self.min, &self.max));
        }
        Ok(())
    }
}

fn range_error<T: PartialOrd + Display>(
    ident: Option<&str>,
    name: Option<&str>,
    data: &T,
    min: &T,
    max: &T,
) -> String {
    let field = name.or(ident).unwrap_or_default();
    format!(
        "{}期望值范围是: {}~{}, 输入的值是: {}",
        field, min, max, data
    )
}

#[cfg(test)]
mod tests {
    use super::RangeValidator;
    use crate::Validator;

    #[test]
    fn test_range_validator() {
        let data = Some(5);
        assert!(
            RangeValidator { min: 1, max: 10 }
                .validate(None, None, &data)
                .is_ok()
        );
    }
}
